package br.usp.ime.ingpos.testes.services;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import br.com.caelum.vraptor.core.Localization;
import br.com.caelum.vraptor.util.test.MockLocalization;
import br.usp.ime.ingpos.modelo.Candidato;
import br.usp.ime.ingpos.modelo.DadosPessoais;
import br.usp.ime.ingpos.modelo.Email;
import br.usp.ime.ingpos.modelo.Inscricao;
import br.usp.ime.ingpos.modelo.ProcessoSeletivo;
import br.usp.ime.ingpos.modelo.TipoProcessoSeletivo;
import br.usp.ime.ingpos.modelo.dao.CandidatoDAO;
import br.usp.ime.ingpos.modelo.dao.UsuarioDao;
import br.usp.ime.ingpos.services.CandidatoService;
import br.usp.ime.ingpos.services.EmailException;
import br.usp.ime.ingpos.services.EmailService;
import br.usp.ime.ingpos.services.UsuarioService;
import br.usp.ime.ingpos.testes.BancoDeDadosTestCase;
import br.usp.ime.ingpos.web.controllers.UsuarioSessao;

public class CandidatoServiceTeste
extends
BancoDeDadosTestCase
{

    private CandidatoService candidatoService;
    private UsuarioSessao usuarioSessao;
    private UsuarioService usuarioService;
    private CandidatoDAO candidatoDAO;
    private EmailService emailService;
    private Localization localization;
    private UsuarioDao usuarioDAO;
    private Email email;

    public CandidatoServiceTeste(
        String name )
    {
        super( name );
    }

    @Before
    protected void setUp() throws Exception
    {
        super.setUp();

        usuarioSessao = new UsuarioSessao();
        usuarioDAO = Mockito.mock( UsuarioDao.class );
        usuarioService = new UsuarioService( usuarioDAO, usuarioSessao );
        candidatoDAO = Mockito.mock( CandidatoDAO.class );
        emailService = Mockito.mock( EmailService.class );
        email = Mockito.mock( Email.class );
        localization = new MockLocalization();
        candidatoService = new CandidatoService(
            usuarioSessao,
            usuarioService,
            candidatoDAO,
            emailService,
            localization );
    }

    @Test
    public void testeSalvar()
    {
        Candidato candidato = new Candidato();
        candidatoService.salvar( candidato );
        Mockito.verify( candidatoDAO ).save( candidato );
    }

    @Test
    public void testeAtualizar()
    {
        Candidato candidato = new Candidato();
        candidatoService.atualizar( candidato );
        Mockito.verify( candidatoDAO ).saveOrUpdate( candidato );
    }

    @Test
    public void testeListaTodos()
    {
        List<Candidato> candidatosEsperados = new ArrayList<Candidato>();
        Mockito.when( candidatoDAO.findAll() ).thenReturn( candidatosEsperados );
        List<Candidato> candidatosRetornados = candidatoService.listaTodos();
        assertEquals( candidatosEsperados, candidatosRetornados );
    }

    @Test
    public void testeEnviarEmailAprovadosVazio()
    {
        candidatoService.enviarEmailAprovados( (List<Inscricao>) new ArrayList<Inscricao>() );
        Mockito.verifyZeroInteractions( emailService );
    }

    @Test
    public void testeEnviarEmailAprovados()
    {
        DadosPessoais dados = Mockito.mock( DadosPessoais.class );
        Mockito.when( dados.getNomeCompleto() ).thenReturn( "Alfredo" );
        List<Inscricao> inscricoes = new ArrayList<Inscricao>();
        Inscricao inscricao1 = Mockito.mock( Inscricao.class );
        Candidato candidato1 = Mockito.mock( Candidato.class );
        Mockito.when( inscricao1.getCandidato() ).thenReturn( candidato1 );
        Mockito.when( candidato1.getEmail() ).thenReturn( "Email1" );
        Mockito.when( candidato1.getDadosPessoais() ).thenReturn( dados );
        Inscricao inscricao2 = Mockito.mock( Inscricao.class );
        Candidato candidato2 = Mockito.mock( Candidato.class );
        Mockito.when( inscricao2.getCandidato() ).thenReturn( candidato2 );
        Mockito.when( candidato2.getEmail() ).thenReturn( "Email2" );
        Mockito.when( candidato2.getDadosPessoais() ).thenReturn( dados );
        Inscricao inscricao3 = Mockito.mock( Inscricao.class );
        Candidato candidato3 = Mockito.mock( Candidato.class );
        Mockito.when( inscricao3.getCandidato() ).thenReturn( candidato3 );
        Mockito.when( candidato3.getEmail() ).thenReturn( "Email3" );
        Mockito.when( candidato3.getDadosPessoais() ).thenReturn( dados );

        ProcessoSeletivo processoSeletivo = Mockito.mock( ProcessoSeletivo.class );
        Mockito.when( processoSeletivo.getTipoProcessoSeletivo() ).thenReturn( TipoProcessoSeletivo.DOUTORADO );
        Mockito.when( inscricao1.getProcessoSeletivo() ).thenReturn( processoSeletivo );
        Mockito.when( inscricao2.getProcessoSeletivo() ).thenReturn( processoSeletivo );
        Mockito.when( inscricao3.getProcessoSeletivo() ).thenReturn( processoSeletivo );

        inscricoes.add( inscricao1 );
        inscricoes.add( inscricao2 );
        inscricoes.add( inscricao3 );

        Mockito.when(
            emailService.construirEmail( Mockito.anyString(), Mockito.anyString(),
                Mockito.anyString() ) ).thenReturn( email );

        candidatoService.enviarEmailAprovados( inscricoes );
        Mockito.verify( candidato1 ).getEmail();
        Mockito.verify( candidato2).getEmail();
        Mockito.verify( candidato3).getEmail();
        Mockito.verify( emailService).construirEmail( Mockito.anyString(), Mockito.anyString(), Mockito.eq("Email1") ) ;
        Mockito.verify( emailService).construirEmail( Mockito.anyString(), Mockito.anyString(), Mockito.eq("Email2") ) ;
        Mockito.verify( emailService).construirEmail( Mockito.anyString(), Mockito.anyString(), Mockito.eq("Email3") );
        try {
            Mockito.verify( emailService, Mockito.times(3)).enviarEmail( email ) ;
        } catch( EmailException e ) {
            assertTrue(false);
            e.printStackTrace();
        }
    }

    @Test (expected=IllegalStateException.class)
    public void testeEnviarEmailAprovadosErro() throws EmailException
    {
        boolean ok = false;
        try {
            DadosPessoais dados = Mockito.mock( DadosPessoais.class );
            Mockito.when( dados.getNomeCompleto() ).thenReturn( "Alfredo" );
            List<Inscricao> inscricoes = new ArrayList<Inscricao>();
            Inscricao inscricao1 = Mockito.mock( Inscricao.class );
            Candidato candidato1 = Mockito.mock( Candidato.class );
            Mockito.when( inscricao1.getCandidato() ).thenReturn( candidato1 );
            Mockito.when( candidato1.getEmail() ).thenReturn( "Email1" );
            Mockito.when( candidato1.getDadosPessoais() ).thenReturn( dados );
            Inscricao inscricao2 = Mockito.mock( Inscricao.class );
            Candidato candidato2 = Mockito.mock( Candidato.class );
            Mockito.when( inscricao2.getCandidato() ).thenReturn( candidato2 );
            Mockito.when( candidato2.getEmail() ).thenReturn( "Email2" );
            Mockito.when( candidato2.getDadosPessoais() ).thenReturn( dados );
            Inscricao inscricao3 = Mockito.mock( Inscricao.class );
            Candidato candidato3 = Mockito.mock( Candidato.class );
            Mockito.when( inscricao3.getCandidato() ).thenReturn( candidato3 );
            Mockito.when( candidato3.getEmail() ).thenReturn( "Email3" );
            Mockito.when( candidato3.getDadosPessoais() ).thenReturn( dados );

            ProcessoSeletivo processoSeletivo = Mockito.mock( ProcessoSeletivo.class );
            Mockito.when( processoSeletivo.getTipoProcessoSeletivo() ).thenReturn( TipoProcessoSeletivo.DOUTORADO );
            Mockito.when( inscricao1.getProcessoSeletivo() ).thenReturn( processoSeletivo );
            Mockito.when( inscricao2.getProcessoSeletivo() ).thenReturn( processoSeletivo );
            Mockito.when( inscricao3.getProcessoSeletivo() ).thenReturn( processoSeletivo );

            inscricoes.add( inscricao1 );
            inscricoes.add( inscricao2 );
            inscricoes.add( inscricao3 );

            Mockito.when(
                emailService.construirEmail( Mockito.anyString(), Mockito.anyString(),
                    Mockito.anyString() ) ).thenReturn( email );
            Mockito.doThrow( new EmailException() ).when( emailService ).enviarEmail( email );
            candidatoService.enviarEmailAprovados( inscricoes );
        } catch (IllegalStateException ise) {
            ok = true;
        }
        assertTrue(ok);
    }

    @Test
    public void testeEnviarEmailReprovados()
    {
        DadosPessoais dados = Mockito.mock( DadosPessoais.class );
        Mockito.when( dados.getNomeCompleto() ).thenReturn( "Alfredo" );
        List<Inscricao> inscricoes = new ArrayList<Inscricao>();
        Inscricao inscricao1 = Mockito.mock( Inscricao.class );
        Candidato candidato1 = Mockito.mock( Candidato.class );
        Mockito.when( inscricao1.getCandidato() ).thenReturn( candidato1 );
        Mockito.when( candidato1.getEmail() ).thenReturn( "Email1" );
        Mockito.when( candidato1.getDadosPessoais() ).thenReturn( dados );
        Inscricao inscricao2 = Mockito.mock( Inscricao.class );
        Candidato candidato2 = Mockito.mock( Candidato.class );
        Mockito.when( inscricao2.getCandidato() ).thenReturn( candidato2 );
        Mockito.when( candidato2.getEmail() ).thenReturn( "Email2" );
        Mockito.when( candidato2.getDadosPessoais() ).thenReturn( dados );
        Inscricao inscricao3 = Mockito.mock( Inscricao.class );
        Candidato candidato3 = Mockito.mock( Candidato.class );
        Mockito.when( inscricao3.getCandidato() ).thenReturn( candidato3 );
        Mockito.when( candidato3.getEmail() ).thenReturn( "Email3" );
        Mockito.when( candidato3.getDadosPessoais() ).thenReturn( dados );

        ProcessoSeletivo processoSeletivo = Mockito.mock( ProcessoSeletivo.class );
        Mockito.when( processoSeletivo.getTipoProcessoSeletivo() ).thenReturn( TipoProcessoSeletivo.DOUTORADO );
        Mockito.when( inscricao1.getProcessoSeletivo() ).thenReturn( processoSeletivo );
        Mockito.when( inscricao2.getProcessoSeletivo() ).thenReturn( processoSeletivo );
        Mockito.when( inscricao3.getProcessoSeletivo() ).thenReturn( processoSeletivo );

        inscricoes.add( inscricao1 );
        inscricoes.add( inscricao2 );
        inscricoes.add( inscricao3 );

        Mockito.when(
            emailService.construirEmail( Mockito.anyString(), Mockito.anyString(),
                Mockito.anyString() ) ).thenReturn( email );

        candidatoService.enviarEmailReprovados( inscricoes );
        Mockito.verify( candidato1 ).getEmail();
        Mockito.verify( candidato2).getEmail();
        Mockito.verify( candidato3).getEmail();
        Mockito.verify( emailService).construirEmail( Mockito.anyString(), Mockito.anyString(), Mockito.eq("Email1") ) ;
        Mockito.verify( emailService).construirEmail( Mockito.anyString(), Mockito.anyString(), Mockito.eq("Email2") ) ;
        Mockito.verify( emailService).construirEmail( Mockito.anyString(), Mockito.anyString(), Mockito.eq("Email3") );
        try {
            Mockito.verify( emailService, Mockito.times(3)).enviarEmail( email ) ;
        } catch( EmailException e ) {
            assertTrue(false);
            e.printStackTrace();
        }
    }

    @Test (expected=IllegalStateException.class)
    public void testeEnviarEmailReprovadosErro() throws EmailException
    {
        boolean ok = false;
        try {
            DadosPessoais dados = Mockito.mock( DadosPessoais.class );
            Mockito.when( dados.getNomeCompleto() ).thenReturn( "Alfredo" );
            List<Inscricao> inscricoes = new ArrayList<Inscricao>();
            Inscricao inscricao1 = Mockito.mock( Inscricao.class );
            Candidato candidato1 = Mockito.mock( Candidato.class );
            Mockito.when( inscricao1.getCandidato() ).thenReturn( candidato1 );
            Mockito.when( candidato1.getEmail() ).thenReturn( "Email1" );
            Mockito.when( candidato1.getDadosPessoais() ).thenReturn( dados );
            Inscricao inscricao2 = Mockito.mock( Inscricao.class );
            Candidato candidato2 = Mockito.mock( Candidato.class );
            Mockito.when( inscricao2.getCandidato() ).thenReturn( candidato2 );
            Mockito.when( candidato2.getEmail() ).thenReturn( "Email2" );
            Mockito.when( candidato2.getDadosPessoais() ).thenReturn( dados );
            Inscricao inscricao3 = Mockito.mock( Inscricao.class );
            Candidato candidato3 = Mockito.mock( Candidato.class );
            Mockito.when( inscricao3.getCandidato() ).thenReturn( candidato3 );
            Mockito.when( candidato3.getEmail() ).thenReturn( "Email3" );
            Mockito.when( candidato3.getDadosPessoais() ).thenReturn( dados );

            ProcessoSeletivo processoSeletivo = Mockito.mock( ProcessoSeletivo.class );
            Mockito.when( processoSeletivo.getTipoProcessoSeletivo() ).thenReturn( TipoProcessoSeletivo.DOUTORADO );
            Mockito.when( inscricao1.getProcessoSeletivo() ).thenReturn( processoSeletivo );
            Mockito.when( inscricao2.getProcessoSeletivo() ).thenReturn( processoSeletivo );
            Mockito.when( inscricao3.getProcessoSeletivo() ).thenReturn( processoSeletivo );

            inscricoes.add( inscricao1 );
            inscricoes.add( inscricao2 );
            inscricoes.add( inscricao3 );

            Mockito.when(
                emailService.construirEmail( Mockito.anyString(), Mockito.anyString(),
                    Mockito.anyString() ) ).thenReturn( email );
            Mockito.doThrow( new EmailException() ).when( emailService ).enviarEmail( email );
            candidatoService.enviarEmailReprovados( inscricoes );
        } catch (IllegalStateException ise) {
            ok = true;
        }
        assertTrue(ok);
    }
}